package top.guoziyang.mydb.backend.dm.page;

import java.util.Arrays;

import top.guoziyang.mydb.backend.dm.pageCache.PageCache;
import top.guoziyang.mydb.backend.utils.Parser;

/**
 * PageX管理普通页
 * 普通页结构
 * [FreeSpaceOffset] [Data]
 * FreeSpaceOffset: 2字节 空闲位置开始偏移
 */
public class PageX {
    
    private static final short OF_FREE = 0;
    private static final short OF_DATA = 2;
    public static final int MAX_FREE_SPACE = PageCache.PAGE_SIZE - OF_DATA;

    /**
     * 初始化一个新的页面，大小为8KB
     * 通过setFSO(raw, OF_DATA)给普通页设置空闲位置的偏移。
     * */
    public static byte[] initRaw() {
        byte[] raw = new byte[PageCache.PAGE_SIZE];
        setFSO(raw, OF_DATA);
        return raw;
    }
    /**
     * 设置空闲位置的偏移.
     * raw: 数据
     * ofData: 空闲位置的偏移
     * */
    private static void setFSO(byte[] raw, short ofData) {
        //将short类型的偏移量ofData转换为字节,存储到前2个字节
        System.arraycopy(Parser.short2Byte(ofData), 0, raw, OF_FREE, OF_DATA);
    }

    /***
     * 获取pg的FSO
     */
    public static short getFSO(Page pg) {
        return getFSO(pg.getData());
    }

    private static short getFSO(byte[] raw) {
        return Parser.parseShort(Arrays.copyOfRange(raw, 0, 2));
    }

    /***
     * 将raw插入pg中，返回插入位置
     */
    public static short insert(Page pg, byte[] raw) {
        // 插入数据了，设为脏页
        pg.setDirty(true);
        // 获取空闲位置的偏移,并插入数据
        short offset = getFSO(pg.getData());
        System.arraycopy(raw, 0, pg.getData(), offset, raw.length);
        // 写入之后更新 FSO
        setFSO(pg.getData(), (short)(offset + raw.length));
        return offset;
    }

    /***
     * 获取页面的空闲空间大小
     */
    public static int getFreeSpace(Page pg) {
        return PageCache.PAGE_SIZE - (int)getFSO(pg.getData());
    }

    /***
     * 将raw插入pg中指定的offset位置，更新pg的offset，保证其始终指向页面内下一个空闲位置
     * 该方法通常用于redo日志中重做插入操作
     */
    public static void recoverInsert(Page pg, byte[] raw, short offset) {
        pg.setDirty(true);
        System.arraycopy(raw, 0, pg.getData(), offset, raw.length);
        // 回放操作中的插入可能出现在已有的数据之前或者覆盖一部分旧数据,通过条件判断来更新FSO
        short rawFSO = getFSO(pg.getData());
        if(rawFSO < offset + raw.length) {
            setFSO(pg.getData(), (short)(offset+raw.length));
        }
    }

    /***
     * 将raw插入pg中指定的offset位置，不更新offset
     */
    public static void recoverUpdate(Page pg, byte[] raw, short offset) {
        pg.setDirty(true);
        System.arraycopy(raw, 0, pg.getData(), offset, raw.length);
    }
}
